using System.Net;
using System.Text;

namespace ipdatacloud_dat
{

    public class IPSearch
    {
        private static readonly Lazy<IPSearch> lazy = new Lazy<IPSearch>(() => new IPSearch());
        public static IPSearch Instance { get { return lazy.Value; } }
        private IPSearch()
        {
            LoadDat();
        }

        private string datPath = "D:\\databases\\ipdatacloud_scenes_mob.dat";

        private DateTime lastRead = DateTime.MinValue;

        private long[,] prefmap = new long[256, 2];

        private string[] addrArr;
        private byte[] data;



        /// <summary>
        /// 初始化二进制  ipdatacloud.dat 数据
        /// </summary>

        private void LoadDat()
        {
            data = File.ReadAllBytes(datPath);

            for (int k = 0; k < 256; k++)
            {
                int i = k * 8 + 4;
                int prefix = k;
                long startIndex = ReadLittleEndian32(data[i], data[i + 1], data[i + 2], data[i + 3]);
                long endIndex = ReadLittleEndian32(data[i + 4], data[i + 5], data[i + 6], data[i + 7]);
                prefmap[k, 0] = startIndex; prefmap[k, 1] = endIndex;
            }


        }



        public string Find(string ip)
        {
            String[] ips = ip.Split(".");
            long pref = long.Parse(ips[0]);
            long val = IpToLong(ip);
            long low = prefmap[pref, 0], high = prefmap[pref, 1];
            long cur = low == high ? low : BinarySearch(low, high, val);

            return cur > -1 ? GetAddr(cur) : "||||||||||||||||||";
        }


        public string GetAddr(long cur)
        {
            long p = 2052 + (cur * 9);
            int offset = data[4 + p] + ((data[5 + p]) << 8) + ((data[6 + p]) << 16) + ((data[7 + p]) << 24);
            int length = data[8 + p];
            return Encoding.UTF8.GetString(data, offset, length);
        }


        
        private long BinarySearch(long low, long high, long k)
        {
            long M = 0, mid = 0;
            while (low <= high)
            {
                mid = (low + high) / 2;

                long p = 2052 + (mid * 9);
                uint endipnum = ReadLittleEndian32(data[p], data[1 + p], data[2 + p], data[3 + p]);

                if (endipnum >= k)
                {
                    M = mid;
                    if (mid == 0)
                    {
                        break;
                    }
                    high = mid - 1;
                }
                else
                    low = mid + 1;
            }
            return M;
        }


        static long IpToLong(string ipAddress)
        {
            IPAddress ip = IPAddress.Parse(ipAddress);

            byte[] bytes = ip.GetAddressBytes();

            Array.Reverse(bytes); 
            return BitConverter.ToUInt32(bytes, 0);
        }



        private uint ReadBigEndian32(byte a, byte b, byte c, byte d)
        {
            return (uint)((a << 24) | (b << 16) | (c << 8) | d);
        }


        private uint ReadLittleEndian32(byte a, byte b, byte c, byte d)
        {
            return (uint)((a | (b << 8) | (c << 16) | (d << 24)));
        }

      
    }


}